package cn.assumejson.pro_netty.proxy;

import cn.assumejson.pro_netty.annotation.RemoteInvoke;
import cn.assumejson.pro_netty.netty.client.NettyClient;
import cn.assumejson.pro_netty.netty.client.asyn.RequestFuture;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import static cn.assumejson.pro_netty.utils.GsonUtil.gson;

/**
 * JDK动态代理实现
 *
 * @author by gang.Li 2022/3/12 16:57
 */
@Component
public class JDKDynamicProxy implements InvocationHandler, BeanPostProcessor {
    private Field target;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //采用netty客户端去需要去调用服务器
        RequestFuture request = new RequestFuture();
        //把接口类名+方法名组装成path
        request.setPath(target.getType().getName() + "." + method.getName());
        //设置参数,此处可以把参数改成数组
        request.setRequest(args[0]);
        //远程调用
        Object resp = NettyClient.sendRequest(request);
        Class<?> returnType = method.getReturnType();
        if (resp == null) {
            return null;
        }
        //返回结果反序列化
//        resp = JSONObject.parseObject(JSONObject.toJSONString(resp), returnType);
        resp = gson.fromJson(gson.toJson(resp), returnType);
        return resp;
    }


    //定义获取代理对象方法
    private Object getJDKProxy(Field field) {
        this.target = field;
        //JDK动态代理只能针接口进行代理
        return Proxy.newProxyInstance(field.getType().getClassLoader(), new Class[]{field.getType()}, this);
    }

    /**
     * 动态代理所有使用{@link RemoteInvoke}注解的方法,实现远程调用
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(RemoteInvoke.class)) {
                field.setAccessible(true);
                try {
                    //对包含有RemoteInvoke注解的属性重新赋值
                    field.set(bean, getJDKProxy(field));
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }


}
